package org.jeecg.modules.third.openapi.utils;

import cn.hutool.http.HttpUtil;
import lombok.extern.log4j.Log4j2;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.*;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@Log4j2
public class httpsCall {

    private static final String HMAC_SHA1 = "HmacSHA1";
    public static final char[] DIGITAL = "0123456789ABCDEF".toCharArray();

    public static String httpsCall(String url, Map<String, Object> param, boolean strict) throws Exception {
        // 1、处理参数
        processParam(param);
        // 2、将 map 参数拼接成 string 格式
        String reqBody = convertMapToString(param);
        log.debug("Request - url: {}, reqBody: {}", url, reqBody);
        if (!strict) {
            // 信任证书
            trustAllCerts();
            // 信任域名
            trustAllHosts();
        }
        // 3、发起调用；
        String post = HttpUtil.post(url, reqBody);
        log.debug("Response - respBody: {} ", post);
        return post;
    }


    /**
     * 处理参数：①移除 srt，②计算签名，③增加 sign；
     * * @param param 原始参数
     */
    private static void processParam(Map<String, Object> param)throws Exception {
        if (!param.containsKey("srt")) {
            throw new NullPointerException();
        }
        param.remove("sign");
        String srt = String.valueOf(param.remove("srt"));
        List<String> paramValueList = new ArrayList<>();
        for (Map.Entry<String, Object> entry : param.entrySet()) {
            paramValueList.add(entry.getKey() + entry.getValue())
            ;
        }
        Collections.sort(paramValueList);
        String[] data = new String[paramValueList.size()];
        paramValueList.toArray(data);
        byte[] signature = hmacSha1(data, srt.getBytes(StandardCharsets.UTF_8));
        String sign = encodeHexStr(signature);
        param.put("sign", sign);
    }

    /**
     * 将参数转换为字符串
     * * @param param 请求参数
     */
    private static String convertMapToString(Map<String, Object> param) {
        StringBuilder sb = new StringBuilder();
        if (param != null && !param.isEmpty()) {
            for (Map.Entry<String, Object> entry : param.entrySet()){
                sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
            sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }

    /**
     * 信任所有证书
     * .
     */
    private static void trustAllCerts() throws Exception {
        TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager(){
        public X509Certificate[] getAcceptedIssuers () {
            return null;
        }
        public void checkClientTrusted (X509Certificate[]certs, String authType){
        }

        public void checkServerTrusted (X509Certificate[]certs, String authType){
        }
        }};
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()
        );
    }

    /**
     * 信任所有域名
     */
    private static void trustAllHosts() {
        HostnameVerifier trustAllHosts = (hostname, session) ->true;
        HttpsURLConnection.setDefaultHostnameVerifier(trustAllHosts);
    }

    /**
     * HmacSha1 定制算法
     * * @param strings 内容
     *
     * @param key key
     * @return 签名
     */
    private static byte[] hmacSha1(String[] strings, byte[] key) throws Exception {
        SecretKeySpec signingKey = new SecretKeySpec(key, HMAC_SHA1);
        Mac mac = Mac.getInstance(HMAC_SHA1);
        mac.init(signingKey);
        for (String data : strings) {
            mac.update(data.getBytes(StandardCharsets.UTF_8));
        }
        return mac.doFinal();
    }

    /**
     * 字节转为 Hex 字符串
     * * @param bytes 字节数组
     *
     * @return Hex 字符串
     */
    private static String encodeHexStr(final byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        char[] result = new char[bytes.length * 2];
        for (int i = 0; i < bytes.length; i++) {
            result[i * 2] = DIGITAL[(bytes[i] & 0xf0) >> 4];
            result[i * 2 + 1] = DIGITAL[bytes[i] & 0x0f];
        }
        return new String(result);
    }


}
